using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;

using GarageGames.Torque.Core;
using GarageGames.Torque.Util;
using GarageGames.Torque.Sim;
using GarageGames.Torque.T3D;
using GarageGames.Torque.SceneGraph;
using GarageGames.Torque.MathUtil;

namespace TimeWar
{

    public enum AiMode
    {
        Idle,
        Chase,
        Avoid,
        FollowPath,
        FollowLeader,
    }

    public enum AiState
    {
        Idle,
        Forward,
        Backward,
    }

    [TorqueXmlSchemaType]
    public class AiComponent : TorqueComponent, ITickObject, IAnimatedObject
    {
        //======================================================
        #region Static methods, fields, constructors
        #endregion

        //======================================================
        #region Constructors
        public AiComponent(string name)
        {
            _friendlyName = name;

            //get the friendly object and his animation component
            TorqueObject friendly =
                TorqueObjectDatabase.Instance.FindObject<TorqueObject>(_friendlyName);

            _componentAnimation = friendly.Components.FindComponent<T3DAnimationComponent>();
            _arrayTargets = new List<TorqueObject>();
            _arrayFriendlies = new List<TorqueObject>();

        }

        public AiComponent()
        {
            _friendlyName = "Friendly";
            _componentAnimation = new T3DAnimationComponent();
            _arrayTargets = new List<TorqueObject>();
            _arrayFriendlies = new List<TorqueObject>();
        }
        #endregion

        //======================================================
        #region Public properties, operators, constants, and enums
        public AiMode AiControlMode
        {
            set { _aiMode = value; }
            get { return _aiMode; }
        }

        public bool Enabled
        {
            get { return _enabled; }
            set { _enabled = value; }
        }

        [TorqueCloneIgnore]
        public List<TorqueObject> Targets
        {
            get { return _arrayTargets; }
            set { _arrayTargets = value; }
        }

        [TorqueCloneIgnore]
        public List<TorqueObject> Friendlies
        {
            get { return _arrayFriendlies; }
            set { _arrayFriendlies = value; }
        }

        public int CurrentTargetIndex
        {
            get { return _currentTargetIndex; }
            set { _currentTargetIndex = value; }
        }

        public float TickInterval
        {
            get { return _tickInterval; }
            set { _tickInterval = value; }
        }

        public int CurrentLeaderIndex
        {
            get { return _currentLeaderIndex; }
            set { _currentLeaderIndex = value; }
        }

        public string FriendlyName
        {
            get { return _friendlyName; }
            set { _friendlyName = value; }
        }
        #endregion

        //======================================================
        #region Public methods

        public virtual void ProcessTick(Move move, float dt)
        {
            // todo: perform processing for component here
            if (!_enabled)
            {
                return;
            }

            if (_tickPeriod <= _tickInterval)
            {
                _tickPeriod++;
                return;
            }

            if (!_initialized)
            {
                SetupAnimations();
                //_rigidComponent = TorqueObjectDatabase.Instance.FindObject<T3DRigidComponent>();
                _initialized = true;
            }

            _tickPeriod = 0;

            if (_moveSpeed == 0)
            {
                _aiMode = AiMode.Idle;
            }
            else
            {
                _aiMode = AiMode.FollowLeader;
            }

            if (_aiMode == AiMode.FollowLeader)
            {
                //get leader position:
                if (_currentLeaderIndex >= 0 && _arrayFriendlies != null && _arrayFriendlies.Count > _currentLeaderIndex)
                {
                    TorqueObject leaderObject = _arrayFriendlies[_currentLeaderIndex];
                    Vector3 leaderPosition = leaderObject.Components.FindComponent<T3DSceneComponent>().Position;

                    if (_SceneComponent.Position.X > leaderPosition.X)
                    {
                        rotX--;
                    }
                    else if (_SceneComponent.Position.X < leaderPosition.X)
                    {
                        rotX++;
                    }
                    if (_SceneComponent.Position.Y > leaderPosition.Y)
                    {
                        rotY--;
                    }
                    else if (_SceneComponent.Position.Y < leaderPosition.Y)
                    {
                        rotY++;
                    }
                }
            }

            //process movement
            Matrix translationMatrix = _SceneComponent.Transform;
            Vector3 right = MatrixUtil.MatrixGetRow(0, ref translationMatrix);
            Vector3 forward = MatrixUtil.MatrixGetRow(1, ref translationMatrix);
            Vector3 vel = (forward * rotX) + (right * rotY) * _moveSpeed;
            _rigidComponent.Velocity = vel;


            switch (_aiMode)
            {
                case AiMode.Idle:
                    _aiState = AiState.Idle;
                    break;

                case AiMode.FollowLeader:
                    if (_arrayFriendlies.Count > _currentLeaderIndex && _arrayFriendlies[_currentLeaderIndex] != null)
                    {
                        T3DSceneComponent componentScene = _arrayFriendlies[_currentLeaderIndex].Components.FindComponent<T3DSceneComponent>();
                        friendlyDistanceVector = Vector3.Subtract(componentScene.Position, _SceneComponent.Position);

                        if (previousDirectionVector.X != 0 && previousDirectionVector.Y != 0 && friendlyDistanceVector.X != 0 && friendlyDistanceVector.Y != 0)
                        {
                            rotX = (float)Math.Atan(previousDirectionVector.X / friendlyDistanceVector.X);
                            rotY = (float)Math.Atan(previousDirectionVector.Y / friendlyDistanceVector.Y);
                            _objAngle = _objAngle - (float)Math.Atan(previousDirectionVector.Z / previousDirectionVector.X);
                            _SceneComponent.Rotation = Quaternion.CreateFromYawPitchRoll(0.0f, 0.0f, _objAngle);
                        }
                        //convert the distance vector into a force vector
                        _rigidComponent.Velocity = friendlyDistanceVector;
                        T3DSceneComponent mySceneComponent = Owner.Components.FindComponent<T3DSceneComponent>();
                        T3DSceneComponent leaderSceneComponent = _arrayFriendlies[_currentLeaderIndex].Components.FindComponent<T3DSceneComponent>();

                        if (Vector3.Distance(mySceneComponent.Position, leaderSceneComponent.Position) > 10f)
                        {
                            _aiState = AiState.Forward;
                        }

                        previousDirectionVector = friendlyDistanceVector;
                    }
                    break;

                case AiMode.Chase:
                    if (_arrayTargets.Count > _currentTargetIndex && _arrayTargets[_currentTargetIndex] != null)
                    {
                        T3DSceneComponent enemySceneComponent = _arrayTargets[_currentTargetIndex].Components.FindComponent<T3DSceneComponent>();
                        if (previousDirectionVector.X != 0 && previousDirectionVector.Y != 0 && friendlyDistanceVector.X != 0 && friendlyDistanceVector.Y != 0)
                        {
                            rotX = (float)Math.Atan(previousDirectionVector.X / targetDistanceVector.X);
                            rotY = (float)Math.Atan(previousDirectionVector.Y / targetDistanceVector.Y);
                            targetDistanceVector = Vector3.Subtract(enemySceneComponent.Position, _SceneComponent.Position);
                        }
                        _rigidComponent.Velocity = targetDistanceVector;
                        T3DSceneComponent mySceneComponent = Owner.Components.FindComponent<T3DSceneComponent>();

                        if (Vector3.Distance(mySceneComponent.Position, enemySceneComponent.Position) > 10f)
                        {
                            _aiState = AiState.Forward;
                        }

                        previousDirectionVector = targetDistanceVector;
                    }
                    break;

            }

            switch (_aiState)
            {
                case AiState.Idle:
                    _currentAnimation = (TSAnimation)_componentAnimation.GetAnimation("Idle");
                    _currentAnimation.Play();
                    break;

                case AiState.Forward:
                    _currentAnimation = (TSAnimation)_componentAnimation.GetAnimation("Forward");
                    _currentAnimation.Play();
                    break;
            }

        }

        public virtual void InterpolateTick(float k)
        {
            // todo: interpolate between ticks as needed here
        }

        public override void CopyTo(TorqueComponent obj)
        {
            base.CopyTo(obj);
            AiComponent obj2 = (AiComponent)obj;
            obj2.Friendlies = new List<TorqueObject>();
            foreach (TorqueObject obj3 in _arrayFriendlies)
            {
                obj2._arrayFriendlies.Add(obj3);
            }
            obj2.Targets = new List<TorqueObject>();
            foreach (TorqueObject obj4 in _arrayTargets)
            {
                obj2._arrayTargets.Add(obj4);
            }
            obj2._tickPeriod = _tickPeriod;
            obj2._enabled = _enabled;
            obj2._currentLeaderIndex = _currentLeaderIndex;
            obj2._currentTargetIndex = _currentTargetIndex;
            obj2._aiMode = _aiMode;
            obj2._friendlyName = _friendlyName;
            obj2._currentAnimation = _currentAnimation;
            obj2._componentAnimation = _componentAnimation;
            obj2._rigidComponent = _rigidComponent;
            
        }

        public void SetupAnimations()
        {
            //Idle Animation
            TSAnimation animationIdle = new TSAnimation();
            animationIdle.ThreadName = "ActionThread";
            animationIdle.SequenceName = "root";
            animationIdle.Name = "Idle";
            _componentAnimation.AddAnimation(animationIdle);

            //Forward Animation
            TSAnimation animationForward = new TSAnimation();
            animationForward.ThreadName = "ActionThread";
            animationForward.SequenceName = "run";
            animationForward.Name = "Forward";
            _componentAnimation.AddAnimation(animationForward);

            //Fall Animation
            TSAnimation animationFall = new TSAnimation();
            animationFall.ThreadName = "ActionThread";
            animationFall.SequenceName = "fall";
            animationFall.Name = "Fall";
            _componentAnimation.AddAnimation(animationFall);

            //Back Animation
            TSAnimation animationBack = new TSAnimation();
            animationBack.ThreadName = "ActionThread";
            animationBack.SequenceName = "back";
            animationBack.Name = "Back";
            _componentAnimation.AddAnimation(animationBack);

            //Left Animation
            TSAnimation animationLeft = new TSAnimation();
            animationLeft.ThreadName = "ActionThread";
            animationLeft.SequenceName = "side";
            animationLeft.Name = "Left";
            animationLeft.TimeScale = -1.0F;
            _componentAnimation.AddAnimation(animationLeft);

            //Right Animation
            TSAnimation animationRight = new TSAnimation();
            animationRight.ThreadName = "ActionThread";
            animationRight.SequenceName = "side";
            animationRight.Name = "Right";
            _componentAnimation.AddAnimation(animationRight);

            //Land Animation
            TSAnimation animationLand = new TSAnimation();
            animationLand.ThreadName = "ActionThread";
            animationLand.SequenceName = "land";
            animationLand.Name = "Land";
            _componentAnimation.AddAnimation(animationLand);

            //Dead Animation
            TSAnimation animationDead = new TSAnimation();
            animationDead.ThreadName = "ActionThread";
            animationDead.SequenceName = "land";
            animationDead.Name = "Dead";
            _componentAnimation.AddAnimation(animationDead);
            _currentAnimation = animationIdle;
        }

        public void UpdateAnimation(float dt)
        {
            if (_currentAnimation != null)
            {
                _currentAnimation.Thread.AdvanceTime(dt);
            }
        }


        #endregion

        //======================================================
        #region Private, protected, internal methods

        protected override bool _OnRegister(TorqueObject owner)
        {
            if (!base._OnRegister(owner))
                return false;

            // todo: perform initialization for the component

            // todo: look up interfaces exposed by other components
            // E.g., 
            // _theirInterface = Owner.Components.GetInterface<ValueInterface<float>>("float", "their interface name");            
            _rigidComponent = Owner.Components.FindComponent<T3DRigidComponent>();
            _SceneComponent = Owner.Components.FindComponent<T3DSceneComponent>();
            _componentAnimation = Owner.Components.FindComponent<T3DAnimationComponent>();

            ProcessList.Instance.AddTickCallback(Owner, this);
            ProcessList.Instance.AddAnimationCallback(Owner, this);

            return true;
        }

        protected override void _OnUnregister()
        {
            // todo: perform de-initialization for the component

            base._OnUnregister();
        }

        protected override void _RegisterInterfaces(TorqueObject owner)
        {
            base._RegisterInterfaces(owner);

            // todo: register interfaces to be accessed by other components
            // E.g.,
            // Owner.RegisterCachedInterface("float", "interface name", this, _ourInterface);
        }


        #endregion

        //======================================================
        #region Private, protected, internal fields
        private List<TorqueObject> _arrayTargets = new List<TorqueObject>();
        private List<TorqueObject> _arrayFriendlies = new List<TorqueObject>();
        private T3DRigidComponent _rigidComponent;
        private T3DSceneComponent _SceneComponent;
        private float _tickInterval = 10;
        private float _tickPeriod = 0;
        private bool _enabled = true;
        private AiMode _aiMode = AiMode.FollowLeader;
        private int _currentTargetIndex = 0;
        private int _currentLeaderIndex = 0;
        private T3DAnimationComponent _componentAnimation;
        private string _friendlyName;
        private TSAnimation _currentAnimation;
        private bool _initialized = false;
        private AiState _aiState = AiState.Idle;
        private Vector3 friendlyDistanceVector;
        private Vector3 targetDistanceVector;
        private Vector3 previousDirectionVector;

        float _turnSpeed = 2.5f;
        float _moveSpeed = 4.5f;
        float _objAngle = 0.0f;
        float rotX = 0;
        float rotY = 0;

        #endregion
    }
}
